import random
from typing import Tuple, List

import matplotlib.pyplot as plt


class MontyHallGame:
    """蒙提霍尔三门问题游戏模拟器"""

    def __init__(self):
        self.doors = [0, 1, 2]  # 三个门的编号

    def play_single_game(self, switch_strategy: bool = True) -> bool:
        """
        进行一次游戏

        Args:
            switch_strategy: True表示交换策略，False表示不交换策略

        Returns:
            bool: True表示获胜，False表示失败
        """
        # 随机选择一个门作为奖品门
        prize_door = random.choice(self.doors)

        # 玩家初始选择一个门
        player_choice = random.choice(self.doors)

        # 主持人打开一个没有奖品且玩家没有选择的门
        available_doors = [door for door in self.doors
                          if door != player_choice and door != prize_door]
        host_opens = random.choice(available_doors)

        # 如果采用交换策略，玩家换到剩下的门
        if switch_strategy:
            remaining_doors = [door for door in self.doors
                             if door != player_choice and door != host_opens]
            final_choice = remaining_doors[0]
        else:
            final_choice = player_choice

        # 判断是否获胜
        return final_choice == prize_door

    def simulate_games(self, num_games: int = 10000) -> Tuple[float, float]:
        """
        模拟多次游戏并统计结果

        Args:
            num_games: 模拟游戏次数

        Returns:
            Tuple[float, float]: (交换策略胜率, 不交换策略胜率)
        """
        switch_wins = 0
        no_switch_wins = 0

        print(f"开始模拟 {num_games} 次游戏...")

        for i in range(num_games):
            # 交换策略
            if self.play_single_game(switch_strategy=True):
                switch_wins += 1

            # 不交换策略
            if self.play_single_game(switch_strategy=False):
                no_switch_wins += 1

            # 显示进度
            if (i + 1) % 1000 == 0:
                print(f"已完成 {i + 1} 次模拟")

        switch_rate = switch_wins / num_games
        no_switch_rate = no_switch_wins / num_games

        return switch_rate, no_switch_rate

    def run_convergence_analysis(self, max_games: int = 50000, step: int = 1000) -> None:
        """
        分析胜率随游戏次数的收敛情况

        Args:
            max_games: 最大游戏次数
            step: 统计步长
        """
        game_counts = []
        switch_rates = []
        no_switch_rates = []

        switch_wins = 0
        no_switch_wins = 0

        print(f"进行收敛性分析，最多 {max_games} 次游戏...")

        for i in range(1, max_games + 1):
            # 交换策略
            if self.play_single_game(switch_strategy=True):
                switch_wins += 1

            # 不交换策略
            if self.play_single_game(switch_strategy=False):
                no_switch_wins += 1

            # 每隔step次记录一次结果
            if i % step == 0:
                game_counts.append(i)
                switch_rates.append(switch_wins / i)
                no_switch_rates.append(no_switch_wins / i)

                print(f"游戏次数: {i}, 交换胜率: {switch_wins/i:.4f}, 不交换胜率: {no_switch_wins/i:.4f}")

        # 绘制收敛图
        self.plot_convergence(game_counts, switch_rates, no_switch_rates)

    def plot_convergence(self, game_counts: List[int],
                        switch_rates: List[float],
                        no_switch_rates: List[float]) -> None:
        """绘制胜率收敛图"""
        plt.figure(figsize=(12, 8))

        plt.subplot(2, 1, 1)
        plt.plot(game_counts, switch_rates, 'b-', label='交换策略', linewidth=2)
        plt.plot(game_counts, no_switch_rates, 'r-', label='不交换策略', linewidth=2)
        plt.axhline(y=2/3, color='b', linestyle='--', alpha=0.7, label='理论值 (交换): 2/3')
        plt.axhline(y=1/3, color='r', linestyle='--', alpha=0.7, label='理论值 (不交换): 1/3')
        plt.xlabel('游戏次数')
        plt.ylabel('胜率')
        plt.title('蒙提霍尔问题：胜率随游戏次数的收敛')
        plt.legend()
        plt.grid(True, alpha=0.3)

        # 绘制胜率差异
        plt.subplot(2, 1, 2)
        rate_diff = [s - n for s, n in zip(switch_rates, no_switch_rates)]
        plt.plot(game_counts, rate_diff, 'g-', label='胜率差异 (交换 - 不交换)', linewidth=2)
        plt.axhline(y=1/3, color='g', linestyle='--', alpha=0.7, label='理论差异: 1/3')
        plt.xlabel('游戏次数')
        plt.ylabel('胜率差异')
        plt.title('交换策略与不交换策略的胜率差异')
        plt.legend()
        plt.grid(True, alpha=0.3)

        plt.tight_layout()
        plt.savefig('monty_hall_convergence.png', dpi=300, bbox_inches='tight')
        plt.show()

    def interactive_demo(self) -> None:
        """交互式演示游戏"""
        print("\n=== 蒙提霍尔三门问题交互式演示 ===")
        print("规则：")
        print("1. 有三个门（0, 1, 2），其中一个门后有奖品")
        print("2. 你选择一个门")
        print("3. 主持人打开一个没有奖品的门")
        print("4. 你可以选择是否换到另一个门")
        print("-" * 40)

        total_games = 0
        wins = 0

        while True:
            # 设置游戏
            prize_door = random.choice(self.doors)

            # 玩家选择
            try:
                player_choice = int(input(f"\n请选择一个门 (0, 1, 2): "))
                if player_choice not in self.doors:
                    print("无效选择，请输入 0, 1 或 2")
                    continue
            except ValueError:
                print("请输入有效数字")
                continue

            # 主持人打开门
            available_doors = [door for door in self.doors
                             if door != player_choice and door != prize_door]
            host_opens = random.choice(available_doors)

            print(f"你选择了门 {player_choice}")
            print(f"主持人打开了门 {host_opens}，里面没有奖品")

            remaining_door = [door for door in self.doors
                            if door != player_choice and door != host_opens][0]

            # 玩家决定是否交换
            switch_input = input(f"是否要换到门 {remaining_door}？ (y/n): ").lower()

            if switch_input == 'y':
                final_choice = remaining_door
                strategy = "交换"
            else:
                final_choice = player_choice
                strategy = "不交换"

            # 公布结果
            total_games += 1
            if final_choice == prize_door:
                wins += 1
                print(f"🎉 恭喜！奖品确实在门 {prize_door} 后面！你选择了{strategy}并获胜！")
            else:
                print(f"😔 很遗憾，奖品在门 {prize_door} 后面，你选择了{strategy}但没有获胜。")

            print(f"当前胜率: {wins}/{total_games} = {wins/total_games:.2%}")

            # 继续游戏
            continue_game = input("\n继续游戏？ (y/n): ").lower()
            if continue_game != 'y':
                break

        print(f"\n游戏结束！最终胜率: {wins}/{total_games} = {wins/total_games:.2%}")


def main():
    """主函数"""
    game = MontyHallGame()

    print("蒙提霍尔三门问题模拟器")
    print("=" * 40)

    while True:
        print("\n请选择模式：")
        print("1. 大量模拟统计")
        print("2. 收敛性分析")
        print("3. 交互式游戏")
        print("4. 退出")

        try:
            choice = int(input("请输入选择 (1-4): "))
        except ValueError:
            print("请输入有效数字")
            continue

        if choice == 1:
            # 大量模拟
            try:
                num_games = int(input("请输入模拟次数 (默认10000): ") or "10000")
            except ValueError:
                num_games = 10000

            switch_rate, no_switch_rate = game.simulate_games(num_games)

            print(f"\n模拟结果（{num_games} 次游戏）：")
            print(f"交换策略胜率: {switch_rate:.4f} ({switch_rate:.2%})")
            print(f"不交换策略胜率: {no_switch_rate:.4f} ({no_switch_rate:.2%})")
            print(f"胜率差异: {switch_rate - no_switch_rate:.4f}")
            print(f"理论预期 - 交换: {2/3:.4f}, 不交换: {1/3:.4f}")

        elif choice == 2:
            # 收敛性分析
            try:
                max_games = int(input("请输入最大游戏次数 (默认50000): ") or "50000")
            except ValueError:
                max_games = 50000

            game.run_convergence_analysis(max_games)

        elif choice == 3:
            # 交互式游戏
            game.interactive_demo()

        elif choice == 4:
            print("感谢使用！")
            break

        else:
            print("无效选择，请重试")


if __name__ == "__main__":
    main()

